#region Using's

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;

using NCCEA = System.Collections.Specialized.NotifyCollectionChangedEventArgs;

#endregion

namespace MyUtils
{
	//public class ObservableTreeCollectionAggregator<TTarget> : ObservableCollectionAggregator
	//{
	//    readonly Func<TTarget, IList<TTarget>> treeSelector;
	//    bool IsTree
	//    {
	//        get
	//        {
	//            return !ReferenceEquals(null, treeSelector);
	//        }
	//    }

	//    public ObservableTreeCollectionAggregator(Func<TTarget, IList<TTarget>> treeSelector)
	//    {
	//        this.treeSelector = treeSelector;
	//    }

	//    Dictionary<TTarget, object> nodes = new Dictionary<TTarget, object>();

	//    protected override void OnCollectionChanged(NCCEA e)
	//    {
	//        switch (e.Action)
	//        {
	//            case NotifyCollectionChangedAction.Add:
	//                AnalyzeSubsOfEveryInList(e.NewItems);
	//                break;
	//            case NotifyCollectionChangedAction.Move:
	//                break;
	//            case NotifyCollectionChangedAction.Remove:
	//                break;
	//            case NotifyCollectionChangedAction.Replace:
	//                break;
	//            case NotifyCollectionChangedAction.Reset:
	//                break;
	//            default:
	//                throw new ApplicationException("Unknown collection event: " + e.Action);
	//        }
	//        base.OnCollectionChanged(e);
	//    }

	//    private void AnalyzeSubsOfEveryInList(IList iList)
	//    {
			
	//    }
	//}

	public sealed class ObservableCollectionAggregator : IList, INotifyCollectionChanged
	{
		private readonly List<IList> _mergedCollections = new List<IList>();

		#region IList Members

		public void Clear()
		{
			throw new InvalidOperationException();
		}

		bool IList.Contains(object value)
		{
			foreach(var obj in this)
			{
				if(obj == value)
				{
					return true;
				}
			}
			return false;
		}

		void ICollection.CopyTo(Array array, int index)
		{
			foreach(var o in this)
			{
				array.SetValue(o, index);
				checked
				{
					index++;
				}
			}
		}

		int IList.Add(object value)
		{
			throw new InvalidOperationException();
		}

		int IList.IndexOf(object value)
		{
			// TODO check IndexOf of every sub-collection. It much more faster, especially on hibrid collections, that implements IndexOf by hash table
			// TODO in case of extreamly performance of IndexOf required, there are overcost solution with updatable local hashtable of all elements in every collection.
			var i = 0;
			foreach(var obj in this)
			{
				if(Equals(obj, value))
				{
					return i;
				}
				i++;
			}
			return -1;
		}

		void IList.Insert(int index, object value)
		{
			throw new InvalidOperationException();
		}

		void IList.Remove(object value)
		{
			throw new InvalidOperationException();
		}

		void IList.RemoveAt(int index)
		{
			throw new InvalidOperationException();
		}

		// Properties
		int ICollection.Count
		{
			get
			{
				var num = 0;
				foreach(var list in _mergedCollections)
				{
					num += list.Count;
				}
				return num;
			}
		}

		bool ICollection.IsSynchronized
		{
			get { return true; }
		}

		object syncRoot = new object();

		object ICollection.SyncRoot
		{
			get { return syncRoot; }
		}

		bool IList.IsFixedSize
		{
			get { return false; }
		}

		bool IList.IsReadOnly
		{
			get { return true; }
		}

		object IList.this[int index]
		{
			get
			{
				var num = 0;
				while((num < _mergedCollections.Count) && (index >= _mergedCollections[num].Count))
				{
					index -= _mergedCollections[num].Count;
					num++;
				}
				return _mergedCollections[num][index];
			}
			set { throw new InvalidOperationException(); }
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			foreach(var list in _mergedCollections)
			{
				foreach(var obj in list)
				{
					yield return obj;
				}
			}
		}

		#endregion

		#region INotifyCollectionChanged Members

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		#endregion

		// Methods
		public void AddCollection(IList collection)
		{
			_mergedCollections.Add(collection);
			var changed = collection as INotifyCollectionChanged;
			if(changed != null)
			{
				changed.CollectionChanged += ChildCollection_Changed;
			}
			OnCollectionChanged(new NCCEA(NotifyCollectionChangedAction.Reset));
		}

		void ChildCollection_Changed(object sender, NCCEA e)
		{
			switch (e.Action)
			{
				case NotifyCollectionChangedAction.Reset:
					OnCollectionChanged(new NCCEA(NotifyCollectionChangedAction.Reset));
					break;
				default:
					var baseIndex = 0;
					for (var i = 0; i < _mergedCollections.Count; i++)
					{
						if (_mergedCollections[i] != sender)
						{
							baseIndex += _mergedCollections[i].Count;
						}
						else
						{
							switch (e.Action)
							{
								case NotifyCollectionChangedAction.Add:
									OnCollectionChanged(e.Action, e.NewItems, e.NewStartingIndex + baseIndex);
									break;
								case NotifyCollectionChangedAction.Move:
									OnCollectionChanged(e.Action, e.OldItems, e.OldStartingIndex + baseIndex, e.NewStartingIndex + baseIndex);
									break;
								case NotifyCollectionChangedAction.Remove:
									OnCollectionChanged(e.Action, e.OldItems, e.OldStartingIndex + baseIndex);
									break;
								case NotifyCollectionChangedAction.Replace:
									OnCollectionChanged(e.Action, e.NewItems, e.OldItems, e.NewStartingIndex + baseIndex);
									break;
								case NotifyCollectionChangedAction.Reset:
									Utils.EnsureCore(false, "Can not get here... it should filtered in first switch");
									break;
								default:
									throw new InvalidEnumArgumentException(@"Unknown collection action: " + e.Action);
							}
						}
					}
					break;
			}
		}

		public void ClearCollections()
		{
			foreach(var list in _mergedCollections.ToArray())
			{
				RemoveCollection(list);
			}
		}

		private void OnCollectionChanged(NotifyCollectionChangedAction action, IList changedItems, int startingIndex)
		{
			OnCollectionChanged(new NCCEA(action, changedItems, startingIndex));
		}

		private void OnCollectionChanged(NotifyCollectionChangedAction action, IList changedItems, int index, int oldIndex)
		{
			OnCollectionChanged(new NCCEA(action, changedItems, index, oldIndex));
		}

		private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems, IList oldItems, int startingIndex)
		{
			OnCollectionChanged(new NCCEA(action, newItems, oldItems, startingIndex));
		}

		void OnCollectionChanged(NCCEA args)
		{
			if(CollectionChanged != null)
			{
				CollectionChanged(this, args);
			}
		}

		public void RemoveCollection(IList collection)
		{
			_mergedCollections.Remove(collection);
			var changed = collection as INotifyCollectionChanged;
			if(changed != null)
			{
				changed.CollectionChanged -= ChildCollection_Changed;
			}
			OnCollectionChanged(new NCCEA(NotifyCollectionChangedAction.Reset));
		}
	}
}